package net.danlew.android.joda.sample;

import ohos.app.Context;
import ohos.global.icu.text.DateFormat;
import ohos.global.icu.text.DateIntervalFormat;
import ohos.global.icu.util.DateInterval;
import ohos.global.resource.NotExistException;
import ohos.global.resource.ResourceManager;
import ohos.global.resource.WrongTypeException;
import org.joda.time.*;
import net.danlew.android.joda.ResourceTable;

import java.io.IOException;
import java.text.FieldPosition;
import java.util.Date;

public class DateUtils {

    private static final int FORMAT_SHOW_TIME = 1;
    private static final int FORMAT_SHOW_WEEKDAY = 2;
    private static final int FORMAT_SHOW_YEAR = 4;
    private static final int FORMAT_NO_YEAR = 8;
    private static final int FORMAT_SHOW_DATE = 16;
    private static final int FORMAT_NO_MONTH_DAY = 32;
    private static final int FORMAT_NO_NOON =512;
    private static final int FORMAT_NO_MIDNIGHT = 2048;
    private static final int FORMAT_ABBREV_TIME = 16384;
    private static final int FORMAT_ABBREV_WEEKDAY =32768;
    private static final int FORMAT_ABBREV_MONTH =65536;
    private static final int FORMAT_NUMERIC_DATE = 131072;
    private static final int FORMAT_ABBREV_RELATIVE = 262144;
    private static final int FORMAT_ABBREV_ALL = 524288;
    //private static final int FORMAT_UTC = android.text.format.DateUtils.FORMAT_UTC; //ABBR_UTC_TZ
    private static  String FORMAT_UTC = DateFormat.ABBR_UTC_TZ;
    private static boolean getBit(int num, int i)
    {
        return ((num & (1 << i)) != 0);//true 表示第i位为1,否则为0
    }

    private static  DateTime EPOCH = new DateTime(0, DateTimeZone.UTC);
    private  static String convert(int flag) {
        switch (flag) {
            case 2:
                return ohos.global.icu.text.DateFormat.WEEKDAY;
            case 4:
                return ohos.global.icu.text.DateFormat.YEAR;
            case 8:
                return DateFormat.DAY;
            case 32:
                return DateFormat.YEAR_MONTH;
            case 32768:
                return DateFormat.ABBR_WEEKDAY;
            case 65536:
                return DateFormat.ABBR_MONTH;
            case 524288:
                return DateFormat.ABBR_MONTH_DAY;

            default:
                return ohos.global.icu.text.DateFormat.HOUR_MINUTE;

        }
    }


    public static String formatDateTime(Context context, ReadablePartial time, String flags) {
//        return DateFormat.format(context, (toMillis(time)), 1);
//        if ((flags & FORMAT_SHOW_TIME) != 0)

        return DateFormat.getPatternInstance(flags).format(new Date(toMillis(time)));
    }



    public static String formatDateTime(Context context, ReadableInstant time, String flags) {
        return DateFormat.getPatternInstance(flags).format(new Date(toMillis(time)));
    }

//
    public static String formatDateRange(Context context, ReadablePartial start, ReadablePartial end, String flags) {
        return formatDateRange( context, toMillis(start), toMillis(end), flags);
    }


    public static String formatDateRange(Context context, ReadableInstant start, ReadableInstant end, String flags) {
        return formatDateRange(context, toMillis(start), toMillis(end), flags);
    }

    private static String formatDateRange(Context context, long startMillis, long endMillis, String flags) {
        // Buffer is needed, otherwise end time is off by 1 crucial second; however, don't do this
        // if they are already equal (that indicates a point in time rather than a range).
        if (startMillis != endMillis) {
            endMillis += 1000;
        }
        DateInterval dtInterval = new DateInterval(startMillis,endMillis);
        DateIntervalFormat dtIntervalFmt = DateIntervalFormat.getInstance(flags);
        StringBuffer str = new StringBuffer("");
        FieldPosition pos = new FieldPosition(0);
        // formatting
         dtIntervalFmt.format(dtInterval, str, pos);
        return dtIntervalFmt.format(dtInterval, str, pos).toString();
    }

    private static long toMillis(ReadablePartial time) {
        return time.toDateTime(EPOCH).getMillis();
    }

    private static long toMillis(ReadableInstant time) {
        DateTime dateTime = time instanceof DateTime ? (DateTime) time : new DateTime(time);
        DateTime utcDateTime = dateTime.withZoneRetainFields(DateTimeZone.UTC);
        return utcDateTime.getMillis();
    }

//    public static String formatElapsedTime(ReadableDuration elapsedDuration) {
//        return formatElapsedTime(null, elapsedDuration);
//    }
//
//
  //  public static String formatElapsedTime(StringBuilder recycle, ReadableDuration elapsedDuration) {
//        return android.text.format.DateUtils.formatElapsedTime(recycle,
//            elapsedDuration.toDuration().toStandardSeconds().getSeconds());
//    } //找不到该函数

    public static boolean isToday(ReadablePartial time) {
        if (!time.isSupported(DateTimeFieldType.dayOfMonth())
            || !time.isSupported(DateTimeFieldType.monthOfYear())
            || !time.isSupported(DateTimeFieldType.year())) {
            throw new IllegalArgumentException("isToday() must be passed a ReadablePartial that supports day of " +
                "month, month of year and year.");
        }

        LocalDate localDate = time instanceof LocalDate ? (LocalDate) time : new LocalDate(time);
        return LocalDate.now().compareTo(localDate) == 0;
    }

    public static boolean isToday(ReadableInstant time) {
        return LocalDate.now().compareTo(new LocalDate(time)) == 0;
    }


    public static CharSequence getRelativeTimeSpanString(Context context, ReadablePartial time) throws NotExistException, WrongTypeException, IOException {
        return getRelativeTimeSpanString(context, time.toDateTime(DateTime.now()));
    }


    public static CharSequence getRelativeTimeSpanString(Context context, ReadableInstant time) throws NotExistException, WrongTypeException, IOException {
        int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH;
//         int flags = 16|4|65536 ;
        return getRelativeTimeSpanString(context, time, flags);
    }


    public static CharSequence getRelativeTimeSpanString(Context context, ReadablePartial time, int flags) throws NotExistException, WrongTypeException, IOException {
        return getRelativeTimeSpanString(context, time.toDateTime(DateTime.now()), flags);
    }


    public static CharSequence getRelativeTimeSpanString(Context context, ReadableInstant time, int flags) throws NotExistException, WrongTypeException, IOException {
        boolean abbrevRelative = (flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0;

        // We set the millis to 0 so we aren't off by a fraction of a second when counting intervals
        DateTime now = DateTime.now(time.getZone()).withMillisOfSecond(0);
        DateTime timeDt = new DateTime(time).withMillisOfSecond(0);
        boolean past = !now.isBefore(timeDt);
        Interval interval = past ? new Interval(timeDt, now) : new Interval(now, timeDt);

        int resId;
        long count;
        if (Minutes.minutesIn(interval).isLessThan(Minutes.ONE)) {
            count = Seconds.secondsIn(interval).getSeconds();
            if (past) {
                if (abbrevRelative) {
                    resId = net.danlew.android.joda.ResourceTable.Plural_joda_time_android_abbrev_num_seconds_ago;
                }
                else {
                    resId = ResourceTable.Plural_joda_time_android_abbrev_num_seconds_ago;
                }
            }
            else {
                if (abbrevRelative) {
                    resId = ResourceTable.Plural_joda_time_android_abbrev_in_num_seconds ;
                }
                else {
                    resId = ResourceTable.Plural_joda_time_android_in_num_seconds;
                }
            }
        }
        else if (Hours.hoursIn(interval).isLessThan(Hours.ONE)) {
            count = Minutes.minutesIn(interval).getMinutes();
            if (past) {
                if (abbrevRelative) {
                    resId = ResourceTable.Plural_joda_time_android_abbrev_num_minutes_ago;
                }
                else {
                    resId = ResourceTable.Plural_joda_time_android_num_minutes_ago;
                }
            }
            else {
                if (abbrevRelative) {
                    resId = ResourceTable.Plural_joda_time_android_abbrev_in_num_minutes;
                }
                else {
                    resId = ResourceTable.Plural_joda_time_android_in_num_minutes;
                }
            }
        }
        else if (Days.daysIn(interval).isLessThan(Days.ONE)) {
            count = Hours.hoursIn(interval).getHours();
            if (past) {
                if (abbrevRelative) {
                    resId = ResourceTable.Plural_joda_time_android_abbrev_num_hours_ago;
                }
                else {
                    resId = ResourceTable.Plural_joda_time_android_num_hours_ago;
                }
            }
            else {
                if (abbrevRelative) {
                    resId = ResourceTable.Plural_joda_time_android_abbrev_in_num_hours;
                }
                else {
                    resId = ResourceTable.Plural_joda_time_android_in_num_hours;
                }
            }
        }
        else if (Weeks.weeksIn(interval).isLessThan(Weeks.ONE)) {
            count = Days.daysIn(interval).getDays();
            if (past) {
                if (abbrevRelative) {
                    resId = ResourceTable.Plural_joda_time_android_abbrev_num_days_ago;
                }
                else {
                    resId = ResourceTable.Plural_joda_time_android_num_days_ago;
                }
            }
            else {
                if (abbrevRelative) {
                    resId = ResourceTable.Plural_joda_time_android_abbrev_in_num_days;
                }
                else {
                    resId = ResourceTable.Plural_joda_time_android_in_num_days;
                }
            }
        }
        else {
//            return formatDateRange(context, time, time, convert(flags));
            return formatDateRange(context, time, time,DateFormat.ABBR_MONTH_DAY );
        }

        String format = context.getResourceManager().getElement(resId).getPluralString (resId,(int) count);
        return String.format(format, count);
    }


    public static CharSequence getRelativeTimeSpanString(Context ctx, ReadablePartial time, boolean withPreposition) throws NotExistException, WrongTypeException, IOException {
        return getRelativeTimeSpanString(ctx, time.toDateTime(DateTime.now()), withPreposition);
    }


    public static CharSequence getRelativeTimeSpanString(Context ctx, ReadableInstant time, boolean withPreposition) throws NotExistException, WrongTypeException, IOException {
        String result;
        LocalDate now = LocalDate.now();
        LocalDate timeDate = new LocalDate(time);

        int prepositionId;
        if (Days.daysBetween(now, timeDate).getDays() == 0) {
            // Same day
            int flags = FORMAT_SHOW_TIME;
            result = formatDateRange(ctx, time, time,ohos.global.icu.text.DateFormat.HOUR_MINUTE);
            prepositionId = net.danlew.android.joda.ResourceTable.String_joda_time_android_preposition_for_time;
        }
        else if (Years.yearsBetween(now, timeDate).getYears() != 0) {
            // Different years
            int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
            result = formatDateRange(ctx, time, time, DateFormat.YEAR_NUM_MONTH_DAY);

            // This is a date (like "10/31/2008" so use the date preposition)
            prepositionId = net.danlew.android.joda.ResourceTable.String_joda_time_android_preposition_for_date;
        }
        else {
            // Default
            int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
            result = formatDateRange(ctx, time, time, DateFormat.ABBR_MONTH_DAY);
            prepositionId = net.danlew.android.joda.ResourceTable.String_joda_time_android_preposition_for_date;
        }

        if (withPreposition) {
            result = ctx.getResourceManager().getElement(prepositionId).getString(result);
        }

        return result;
    }



    public static CharSequence getRelativeDateTimeString(Context context, ReadablePartial time,
                                                         ReadablePeriod transitionResolution, int flags) throws NotExistException, WrongTypeException, IOException {
        if (!time.isSupported(DateTimeFieldType.hourOfDay())
            || !time.isSupported(DateTimeFieldType.minuteOfHour())) {
            throw new IllegalArgumentException("getRelativeDateTimeString() must be passed a ReadablePartial that " +
                "supports time, otherwise it makes no sense");
        }

        return getRelativeDateTimeString(context, time.toDateTime(DateTime.now()), transitionResolution, flags);
    }


    public static CharSequence getRelativeDateTimeString(Context context, ReadableInstant time,
                                                         ReadablePeriod transitionResolution, int flags) throws NotExistException, WrongTypeException, IOException {
        ResourceManager r = context.getResourceManager();

        // We set the millis to 0 so we aren't off by a fraction of a second when counting duration
        DateTime now = DateTime.now(time.getZone()).withMillisOfSecond(0);
        DateTime timeDt = new DateTime(time).withMillisOfSecond(0);
        boolean past = !now.isBefore(timeDt);
        Duration duration = past ? new Duration(timeDt, now) : new Duration(now, timeDt);

        // getRelativeTimeSpanString() doesn't correctly format relative dates
        // above a week or exact dates below a day, so clamp
        // transitionResolution as needed.
        Duration transitionDuration;
        Duration minDuration = Days.ONE.toPeriod().toDurationTo(timeDt);
        if (transitionResolution == null) {
            transitionDuration = minDuration;
        }
        else {
            transitionDuration = past ? transitionResolution.toPeriod().toDurationTo(now) :
                transitionResolution.toPeriod().toDurationFrom(now);
            Duration maxDuration = Weeks.ONE.toPeriod().toDurationTo(timeDt);
            if (transitionDuration.isLongerThan(maxDuration)) {
                transitionDuration = maxDuration;
            }
            else if (transitionDuration.isShorterThan(minDuration)) {
                transitionDuration = minDuration;
            }
        }

        CharSequence timeClause = formatDateRange(context, time, time, ohos.global.icu.text.DateFormat.HOUR_MINUTE);

        String result;
        if (!duration.isLongerThan(transitionDuration)) {
            CharSequence relativeClause = getRelativeTimeSpanString(context, time, flags);
            result = r.getElement(net.danlew.android.joda.ResourceTable.String_joda_time_android_relative_time).getString( relativeClause, timeClause);
        }
        else {
            CharSequence dateClause = getRelativeTimeSpanString(context, time, false);

            result = r.getElement(net.danlew.android.joda.ResourceTable.String_joda_time_android_relative_time).getString( dateClause, timeClause);
        }

        return result;
    }

    /**
     * Return given duration in a human-friendly format. For example, "4
     * minutes" or "1 second". Returns only largest meaningful unit of time,
     * from seconds up to hours.
     *
     * The longest duration it supports is hours.
     *
     * This method assumes that there are 60 minutes in an hour,
     * 60 seconds in a minute and 1000 milliseconds in a second.
     * All currently supplied chronologies use this definition.
     */
    public static CharSequence formatDuration(Context context, ReadableDuration readableDuration) throws IOException, NotExistException, WrongTypeException {
        ResourceManager res = context.getResourceManager();
        Duration duration = readableDuration.toDuration();

        final int hours = (int) duration.getStandardHours();
        if (hours != 0) {
            return res.getElement(net.danlew.android.joda.ResourceTable.Plural_joda_time_android_duration_hours).getPluralString( hours, hours);
        }

        final int minutes = (int) duration.getStandardMinutes();
        if (minutes != 0) {

            return res.getElement(net.danlew.android.joda.ResourceTable.Plural_joda_time_android_duration_minutes).getPluralString( minutes, minutes);
        }

        final int seconds = (int) duration.getStandardSeconds();

        return res.getElement(net.danlew.android.joda.ResourceTable.Plural_joda_time_android_duration_seconds).getPluralString( seconds, seconds);
    }

}
